import Axios, { type AxiosInstance, type AxiosRequestConfig } from "axios";
import type { PureHttpError, RequestMethods, PureHttpResponse, PureHttpRequestConfig } from "./types.d";
import { stringify } from "qs";
import NProgress from "../progress";
import { getToken, formatToken } from "@/utils/auth";
import { useUserStoreHook } from "@/store/modules/user";
import globalConfig from "@/config/globalConfig";
import { ElMessage } from "element-plus";

// 相关配置请参考：www.axios-js.com/zh-cn/docs/#axios-request-config-1
const defaultConfig: AxiosRequestConfig = {
    baseURL: globalConfig.baseUrl,
    // 请求超时时间
    timeout: 10000,
    headers: {
        Accept: "application/json, text/plain, */*",
        "Content-Type": "application/json",
        "X-Requested-With": "XMLHttpRequest"
    },
    // 数组格式参数序列化（https://github.com/axios/axios/issues/5142）
    paramsSerializer: {
        serialize: params => {
            return stringify(params, {
                arrayFormat: "comma",
                encode: false // 不进行 URL 编码，避免逗号被转义
            });
        }
    }
};

class PureHttp {
    constructor() {
        this.httpInterceptorsRequest();
        this.httpInterceptorsResponse();
    }

    /** `token`过期后，暂存待执行的请求 */
    private static requests = [];

    /** 防止重复刷新`token` */
    private static isRefreshing = false;

    /** 初始化配置对象 */
    private static initConfig: PureHttpRequestConfig = {};

    /** 保存当前`Axios`实例对象 */
    private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);

    /** 重连原始请求 */
    private static retryOriginalRequest(config: PureHttpRequestConfig) {
        return new Promise(resolve => {
            PureHttp.requests.push((token: string) => {
                config.headers["Authorization"] = formatToken(token);
                resolve(config);
            });
        });
    }

    /**
     * 处理参数中的数组，转换为逗号分隔的字符串
     * 注意：不再需要手动转换，由 paramsSerializer 处理
     */
    private processParams<P>(params?: AxiosRequestConfig<P>): AxiosRequestConfig<P> {
        if (!params?.params || typeof params.params !== "object") {
            return params;
        }

        // 直接返回原参数，让 paramsSerializer 处理数组格式
        return params;
    }

    /** 请求拦截 */
    private httpInterceptorsRequest(): void {
        PureHttp.axiosInstance.interceptors.request.use(
            async (config: PureHttpRequestConfig): Promise<any> => {
                // 开启进度条动画
                NProgress.start();

                // 移除空值参数
                if (config.params) {
                    const filteredParams = {};
                    Object.keys(config.params).forEach(key => {
                        const value = config.params[key];
                        // 只保留非空值（排除 null, undefined, 空字符串）
                        if (value !== null && value !== undefined && value !== "") {
                            filteredParams[key] = value;
                        }
                    });
                    config.params = filteredParams;
                }

                // 优先判断post/get等方法是否传入回调，否则执行初始化设置等回调
                if (typeof config.beforeRequestCallback === "function") {
                    config.beforeRequestCallback(config);
                    return config;
                }
                if (PureHttp.initConfig.beforeRequestCallback) {
                    PureHttp.initConfig.beforeRequestCallback(config);
                    return config;
                }
                /** 请求白名单，放置一些不需要`token`的接口（通过设置请求白名单，防止`token`过期后再请求造成的死循环问题） */
                const whiteList = ["/refresh-token", "/login"];
                return whiteList.some(url => config.url.endsWith(url))
                    ? config
                    : new Promise(resolve => {
                          const data = getToken();
                          if (data) {
                              const now = new Date().getTime();
                              const expired = parseInt(data.expires) - now <= 0;
                              if (expired) {
                                  if (!PureHttp.isRefreshing) {
                                      PureHttp.isRefreshing = true;
                                      // token过期刷新
                                      useUserStoreHook()
                                          .handRefreshToken({
                                              refreshToken: data.refreshToken
                                          })
                                          .then(res => {
                                              const token = res.data.accessToken;
                                              config.headers["Authorization"] = formatToken(token);
                                              PureHttp.requests.forEach(cb => cb(token));
                                              PureHttp.requests = [];
                                          })
                                          .finally(() => {
                                              PureHttp.isRefreshing = false;
                                          });
                                  }
                                  resolve(PureHttp.retryOriginalRequest(config));
                              } else {
                                  config.headers["Authorization"] = formatToken(data.accessToken);
                                  resolve(config);
                              }
                          } else {
                              resolve(config);
                          }
                      });
            },
            error => {
                return Promise.reject(error);
            }
        );
    }

    /** 响应拦截 */
    private httpInterceptorsResponse(): void {
        const instance = PureHttp.axiosInstance;
        instance.interceptors.response.use(
            (response: PureHttpResponse) => {
                const $config = response.config;
                // 关闭进度条动画
                NProgress.done();
                // 优先判断post/get等方法是否传入回调，否则执行初始化设置等回调
                if (typeof $config.beforeResponseCallback === "function") {
                    $config.beforeResponseCallback(response);
                    return response.data;
                }

                // 检查响应数据中的 code 字段
                if (response.data && response.data.code !== 200) {
                    ElMessage.error(response.data.msg || "请求失败");
                    return Promise.reject(response.data);
                }

                if (PureHttp.initConfig.beforeResponseCallback) {
                    PureHttp.initConfig.beforeResponseCallback(response);
                    return response.data;
                }
                return response.data;
            },
            (error: PureHttpError) => {
                const $error = error;
                if ($error.status === 401) {
                    ElMessage.error("登录失效");
                    useUserStoreHook().logOut();
                }

                $error.isCancelRequest = Axios.isCancel($error);
                // 关闭进度条动画
                NProgress.done();
                // 所有的响应异常 区分来源为取消请求/非取消请求
                return Promise.reject($error);
            }
        );
    }

    /** 通用请求工具函数 */
    public request<T>(
        method: RequestMethods,
        url: string,
        param?: AxiosRequestConfig,
        axiosConfig?: PureHttpRequestConfig
    ): Promise<T> {
        const config = {
            method,
            url,
            ...param,
            ...axiosConfig
        } as PureHttpRequestConfig;
        // 单独处理自定义请求/响应回调
        return new Promise((resolve, reject) => {
            PureHttp.axiosInstance
                .request(config)
                .then((response: undefined) => {
                    resolve(response);
                })
                .catch(error => {
                    reject(error);
                });
        });
    }

    /** 单独抽离的`post`工具函数 */
    public post<T, P>(url: string, params?: AxiosRequestConfig<P>, config?: PureHttpRequestConfig): Promise<T> {
        return this.request<T>("post", url, params, config);
    }

    /** 单独抽离的`delete`工具函数 */
    public delete<T, P>(url: string, params?: AxiosRequestConfig<P>, config?: PureHttpRequestConfig): Promise<T> {
        const processedParams = this.processParams(params);
        return this.request<T>("delete", url, processedParams, config);
    }

    /** 单独抽离的`put`工具函数 */
    public put<T, P>(url: string, params?: AxiosRequestConfig<P>, config?: PureHttpRequestConfig): Promise<T> {
        return this.request<T>("put", url, params, config);
    }

    /** 单独抽离的`get`工具函数 */
    public get<T, P>(url: string, params?: AxiosRequestConfig<P>, config?: PureHttpRequestConfig): Promise<T> {
        const processedParams = this.processParams(params);
        return this.request<T>("get", url, processedParams, config);
    }
}

export const http = new PureHttp();
